//#include <NTDDK.h>
#include <ifs/wnet/ntifs.h>
//#include <wdm.h>

VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);
NTSTATUS DriverEntry (
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath);

#pragma alloc_text(init, DriverEntry)
#pragma alloc_text(page, DriverUnload)

typedef struct
{
	PDEVICE_OBJECT obj;
} filter_ext;

#if 0
static NTSTATUS ignore(IN struct _DEVICE_OBJECT *DeviceObject, IN struct _IRP *Irp)
{
	PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);

	KdPrint(("%s type = %d, UserBuffer = %p\n", __FUNCTION__, Irp->Type, Irp->UserBuffer));		
	KdPrint(("%s[%d][%d] control[%d] DeviceObject = %p, FileObject = %p\n", __FUNCTION__,
			pIrpStack->MajorFunction, pIrpStack->MinorFunction, pIrpStack->Control,
			pIrpStack->DeviceObject, pIrpStack->FileObject));			

	IoCompleteRequest(Irp, IO_NO_INCREMENT);	
	return STATUS_SUCCESS;
}

static NTSTATUS loop_read(IN struct _DEVICE_OBJECT *DeviceObject, IN struct _IRP *Irp)
{
	PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
	
	KdPrint(("%s type = %d, UserBuffer = %p\n", __FUNCTION__, Irp->Type, Irp->UserBuffer));
	KdPrint(("%s[%d][%d] control[%d] DeviceObject = %p, FileObject = %p\n", __FUNCTION__,
			pIrpStack->MajorFunction, pIrpStack->MinorFunction, pIrpStack->Control,
			pIrpStack->DeviceObject, pIrpStack->FileObject));			

	if (Irp->UserBuffer)
	{
		KdPrint(("%s type = %d, UserBuffer = %s\n", __FUNCTION__, Irp->Type, Irp->UserBuffer));
		memcpy(Irp->UserBuffer, __FUNCTION__, strlen(__FUNCTION__) + 1);
	}
	IoCompleteRequest(Irp, IO_NO_INCREMENT);	
	return STATUS_SUCCESS;
}

static NTSTATUS loop_write(IN struct _DEVICE_OBJECT *DeviceObject, IN struct _IRP *Irp)
{
	PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);	

	KdPrint(("%s type = %d, UserBuffer = %p\n", __FUNCTION__, Irp->Type, Irp->UserBuffer));
	KdPrint(("%s[%d][%d] control[%d] DeviceObject = %p, FileObject = %p\n", __FUNCTION__,
			pIrpStack->MajorFunction, pIrpStack->MinorFunction, pIrpStack->Control,
			pIrpStack->DeviceObject, pIrpStack->FileObject));			

	IoCompleteRequest(Irp, IO_NO_INCREMENT);	
	return STATUS_SUCCESS;
}
#endif

NTSTATUS GenericCompletion(
				IN PDEVICE_OBJECT pDevObj,
				IN PIRP Irp,
				IN PVOID pContext )
{
	PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
	
	if (Irp->PendingReturned)
		IoMarkIrpPending(Irp);

	KdPrint(("%s[%d][%d] control[%d] stack = %p\n", __FUNCTION__,
			pIrpStack->MajorFunction, pIrpStack->MinorFunction, pIrpStack));			
	
	return STATUS_SUCCESS;
}

VOID driver_fs_notify(IN struct _DEVICE_OBJECT *DeviceObject, IN BOOLEAN FsActive)
{
	NTSTATUS ret;
	char buf[256];
	ULONG retLen;

	ret = ObQueryNameString(DeviceObject, (POBJECT_NAME_INFORMATION)buf, sizeof(buf), &retLen);
	if (ret == STATUS_SUCCESS)
	{
		KdPrint(("filterloop : %s : ObQueryNameString success, len = %d, name = %S\n", __FUNCTION__, retLen,
				((POBJECT_NAME_INFORMATION)buf)->Name.Buffer));		
	}
	else
	{
		KdPrint(("filterloop : %s : ObQueryNameString fail[0x%x]\n", __FUNCTION__, ret));
	}
}

NTSTATUS pass_through(IN struct _DEVICE_OBJECT *DeviceObject, IN struct _IRP *Irp)
{
	filter_ext *ext;
	NTSTATUS ret = STATUS_SUCCESS;

	PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
	
	KdPrint(("%s type = %d, UserBuffer = %p, stack = %p\n", __FUNCTION__, Irp->Type, Irp->UserBuffer, pIrpStack));
	KdPrint(("%s[%d][%d] control[%d] DeviceObject = %p, FileObject = %p\n", __FUNCTION__,
			pIrpStack->MajorFunction, pIrpStack->MinorFunction, pIrpStack->Control,
			pIrpStack->DeviceObject, pIrpStack->FileObject));			
	
//	IoSkipCurrentIrpStackLocation(Irp);
    IoCopyCurrentIrpStackLocationToNext(Irp);
	
	ext = (filter_ext *)(DeviceObject->DeviceExtension);

	IoSetCompletionRoutine(
			Irp,
			GenericCompletion,
			NULL,
			TRUE, TRUE, TRUE );
	
	ret = IoCallDriver(ext->obj, Irp);

//  can not call it
//	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	KdPrint(("%s stack = %p end\n", __FUNCTION__, pIrpStack));
	return ret;
}

static UNICODE_STRING link_name;
static PDEVICE_OBJECT filter_obj = NULL;

VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
	IoUnregisterFsRegistrationChange(pDriverObject, driver_fs_notify);
	IoDeleteSymbolicLink(&link_name);
	if (filter_obj) {
		IoDetachDevice(((filter_ext *)(filter_obj->DeviceExtension))->obj);
		IoDeleteDevice(filter_obj);
	}
	filter_obj = NULL;
}

NTSTATUS DriverEntry (
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath	)
{
	NTSTATUS ret;
	UNICODE_STRING name, target_name;
	int i;
	PDEVICE_OBJECT target_obj = NULL;
	PFILE_OBJECT file_obj;
	
	pDriverObject->DriverUnload = DriverUnload;

	ret = IoRegisterFsRegistrationChange(pDriverObject, driver_fs_notify);
	if (ret != STATUS_SUCCESS) {
		KdPrint(("filterloop : %s : IoRegisterFsRegistrationChange fail[0x%x]\n", __FUNCTION__, ret));
		return STATUS_SUCCESS;	
	}
	
	RtlInitUnicodeString(&target_name, L"\\FileSystem\\testloop");
	ret = IoGetDeviceObjectPointer(&target_name, FILE_ALL_ACCESS, &file_obj, &target_obj);
	if (ret != STATUS_SUCCESS) {
		KdPrint(("filterloop : %s : IoGetDeviceObjectPointer fail[0x%x]\n", __FUNCTION__, ret));
		return STATUS_SUCCESS;	
	}
	ObDereferenceObject(file_obj);
	
/*
	for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i) {
		pDriverObject->MajorFunction[i] = pass_through;
	}
*/	
	RtlInitUnicodeString(&name, L"\\FileSystem\\filter_loop");
//	ret = IoCreateDevice(pDriverObject, sizeof(filter_ext), &name, FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN, FALSE, &dev_obj);
	ret = IoCreateDevice(pDriverObject, sizeof(filter_ext), &name, /*ntfs_obj->DeviceType*/FILE_DEVICE_UNKNOWN, 0, FALSE, &filter_obj);
	if (ret != STATUS_SUCCESS) {
		KdPrint(("filterloop : %s : IoCreateDevice fail[0x%x]\n", __FUNCTION__, ret));
		filter_obj = NULL;
		return STATUS_SUCCESS;	
	}

	RtlInitUnicodeString(&link_name, L"\\??\\filterloop_link");
	IoDeleteSymbolicLink(&link_name);
	ret = IoCreateSymbolicLink(&link_name, &name);
	if (ret != STATUS_SUCCESS) {
		KdPrint(("filterloop : %s : IoCreateSymbolicLink fail[0x%x]\n", __FUNCTION__, ret));
		return STATUS_SUCCESS;	
	}

	ret = IoAttachDeviceToDeviceStackSafe(filter_obj, target_obj, &target_obj);
	if (ret != STATUS_SUCCESS) {
		KdPrint(("filterloop : %s : IoAttachDeviceToDeviceStackSafe fail[0x%x]\n", __FUNCTION__, ret));
		return STATUS_SUCCESS;	
	}
	((filter_ext *)(filter_obj->DeviceExtension))->obj = target_obj;

    if (FlagOn(target_obj->Flags, DO_BUFFERED_IO )) {
        SetFlag(filter_obj->Flags, DO_BUFFERED_IO );
    }

    if (FlagOn(target_obj->Flags, DO_DIRECT_IO )) {
        SetFlag(filter_obj->Flags, DO_DIRECT_IO );
    }

    if (FlagOn(target_obj->Characteristics, FILE_DEVICE_SECURE_OPEN ) ) {
        SetFlag(filter_obj->Characteristics, FILE_DEVICE_SECURE_OPEN );
    }


	pDriverObject->MajorFunction[IRP_MJ_CREATE] = pass_through;
	pDriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] = pass_through;
	pDriverObject->MajorFunction[IRP_MJ_CREATE_MAILSLOT] = pass_through;
	pDriverObject->MajorFunction[IRP_MJ_READ] = pass_through;
	pDriverObject->MajorFunction[IRP_MJ_WRITE] = pass_through;


/* todo	
	pDriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = sfilter_fs_ctrl;
	pDriverObject->MajorFunction[IRP_MJ_CLOSE] = sfilter_close;
	pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = sfilter_cleanup;
*/
	KdPrint(("filterloop : %s success\n", __FUNCTION__));	
	return STATUS_SUCCESS;
}




